package wci.util;

import java.util.ArrayList;
import java.util.Collection;

import wci.intermediate.Definition;
import wci.intermediate.SymTab;
import wci.intermediate.SymTabEntry;
import wci.intermediate.SymTabStack;
import wci.intermediate.TypeForm;
import wci.intermediate.TypeSpec;
import wci.intermediate.symtabimpl.DefinitionImpl;
import wci.intermediate.symtabimpl.SymTabKeyImpl;
import wci.intermediate.typeimpl.TypeFormImpl;
import wci.intermediate.typeimpl.TypeKeyImpl;

/**
 * <h1>CrossReferencer</h1>
 *
 * <p>生成一个交叉引用列表</p>
 *
 * <p>Copyright (c) 2009 by Ronald Mak</p>
 * <p>For instructional purposes only.  No warranties.</p>
 */
public class CrossReferencer
{
    private static final int NAME_WIDTH = 12;

    private static final String NAME_FORMAT       = "%-" + NAME_WIDTH + "s";
    private static final String NUMBERS_LABEL     = "  所在行位置";
    private static final String NUMBERS_UNDERLINE = " ------------    ";
    private static final String NUMBER_FORMAT = " %03d";

    private static final int LABEL_WIDTH  = NUMBERS_LABEL.length();
    private static final int INDENT_WIDTH = NAME_WIDTH + LABEL_WIDTH+10;;

    private static final StringBuilder INDENT = new StringBuilder(INDENT_WIDTH);
    static {
        for (int i = 0; i < INDENT_WIDTH; ++i) INDENT.append(" ");
    }

    /**
     * 打印交叉引用列表
     * @param symTabStack 符号表堆栈
     */
    public void print(SymTabStack symTabStack)
    {
        System.out.println("\n============ 交叉引用列表  ========");
        SymTabEntry programId = symTabStack.getProgramId();
        printRoutine(programId);
    }
    /**
     * 打印程式
     * @param routineId 程式的符号表项
     */
    private void printRoutine(SymTabEntry routineId)
    {
        Definition definition = routineId.getDefinition();
        System.out.println("\n*** " + definition +
                           " " + routineId.getName() + " ***");
        printColumnHeadings();

        // 打印程式中包含的符号表项
        SymTab symTab = (SymTab) routineId.getAttribute(SymTabKeyImpl.ROUTINE_SYMTAB);
        ArrayList<TypeSpec> newRecordTypes = new ArrayList<TypeSpec>();
        printSymTab(symTab, newRecordTypes);

        //打印程式中定义的每一个记录类型
        if (newRecordTypes.size() > 0) {
            printRecords(newRecordTypes);
        }

        //程式中嵌套的程式(过程和函数）
        ArrayList<SymTabEntry> routineIds =
            (ArrayList<SymTabEntry>) routineId.getAttribute(SymTabKeyImpl.ROUTINE_ROUTINES);
        if (routineIds != null) {
            for (SymTabEntry rtnId : routineIds) {
                printRoutine(rtnId);
            }
        }
    }
    /**
     * 打印符号表中的每个项
     * @param symTab 符号表
     * @param recordTypes the list to fill with RECORD type specifications.
     */
    private void printSymTab(SymTab symTab, ArrayList<TypeSpec> recordTypes)
    {
        //有序遍历表中的项
        Collection<SymTabEntry> sorted = symTab.sortedEntries();
        for (SymTabEntry entry : sorted) {
            ArrayList<Integer> lineNumbers = entry.getLineNumbers();

            // 对于每个项，打出标识符名称，后续跟着行数
            System.out.print(String.format(NAME_FORMAT, entry.getName()));
            if (lineNumbers != null) {
                for (Integer lineNumber : lineNumbers) {
                    System.out.print(String.format(NUMBER_FORMAT, lineNumber));
                }
            }

            //打印表项本身
            System.out.println();
            printEntry(entry, recordTypes);
        }
    }
    /**
     * 打印符号表项
     * @param entry 项
     * @param recordTypes 记录类型说明列表
     */
    private void printEntry(SymTabEntry entry, ArrayList<TypeSpec> recordTypes)
    {
        Definition definition = entry.getDefinition();
        int nestingLevel = entry.getSymTab().getNestingLevel();
        System.out.println(INDENT + "定义: " + definition.getText());
        System.out.println(INDENT + "嵌套层级: " + nestingLevel);

        // 打印类型说明
        TypeSpec type = entry.getTypeSpec();
        printType(type);

        switch ((DefinitionImpl) definition) {
        	//该项是常量
            case CONSTANT: {
                Object value = entry.getAttribute(SymTabKeyImpl.CONSTANT_VALUE);
                System.out.println(INDENT + "值为 " + valueToString(value));

                //仅在类型匿名的情况下才打印类型详细信息。
                if (type.getIdentifier() == null) {
                    printTypeDetail(type, recordTypes);
                }

                break;
            }
            //枚举常量
            case ENUMERATION_CONSTANT: {
                Object value = entry.getAttribute(SymTabKeyImpl.CONSTANT_VALUE);
                System.out.println(INDENT + "值为 " + valueToString(value));

                break;
            }
            //类型定义
            case TYPE: {

                // 仅在此类型第一次定义的时候打印详细信息
                if (entry == type.getIdentifier()) {
                    printTypeDetail(type, recordTypes);
                }

                break;
            }

            case VARIABLE: {

                // 仅在变量匿名的时候才打印详细类型信息
                if (type.getIdentifier() == null) {
                    printTypeDetail(type, recordTypes);
                }

                break;
            }
        }
    }
    
    /**
     * 打印类型说明
     * @param type 类型
     */
    private void printType(TypeSpec type)
    {
        if (type != null) {
            TypeForm form = type.getForm();
            SymTabEntry typeId = type.getIdentifier();
            String typeName = typeId != null ? typeId.getName() : "<匿名>";

            System.out.println(INDENT + "类型格式 = " + form +
                               ", 类型ID = " + typeName);
        }
    }
    private static final String ENUM_CONST_FORMAT = "%" + NAME_WIDTH + "s = %s";

    /**
     * 打印类型的详细信息
     * @param type 类型
     * @param recordTypes 附带的记录类型列表
     */
    private void printTypeDetail(TypeSpec type, ArrayList<TypeSpec> recordTypes)
    {
        TypeForm form = type.getForm();

        switch ((TypeFormImpl) form) {

            case ENUMERATION: {
                ArrayList<SymTabEntry> constantIds = (ArrayList<SymTabEntry>)
                    type.getAttribute(TypeKeyImpl.ENUMERATION_CONSTANTS);

                System.out.println(INDENT + "--- 枚举常量 ---");

                // 打印每个枚举常量和它的值
                for (SymTabEntry constantId : constantIds) {
                    String name = constantId.getName();
                    Object value = constantId.getAttribute(SymTabKeyImpl.CONSTANT_VALUE);

                    System.out.println(INDENT + String.format(ENUM_CONST_FORMAT,
                                                              name, value));
                }

                break;
            }

            case SUBRANGE: {
                Object minValue = type.getAttribute(TypeKeyImpl.SUBRANGE_MIN_VALUE);
                Object maxValue = type.getAttribute(TypeKeyImpl.SUBRANGE_MAX_VALUE);
                TypeSpec baseTypeSpec =
                    (TypeSpec) type.getAttribute(TypeKeyImpl.SUBRANGE_BASE_TYPE);

                System.out.println(INDENT + "--- 基类型  ---");
                printType(baseTypeSpec);

                //仅仅在类型匿名的时候才打印基类型信息
                if (baseTypeSpec.getIdentifier() == null) {
                    printTypeDetail(baseTypeSpec, recordTypes);
                }

                System.out.print(INDENT + "范围  = ");
                System.out.println(valueToString(minValue) + ".." +
                                   valueToString(maxValue));

                break;
            }

            case ARRAY: {
                TypeSpec indexType =
                    (TypeSpec) type.getAttribute(TypeKeyImpl.ARRAY_INDEX_TYPE);
                TypeSpec elementType =
                    (TypeSpec) type.getAttribute(TypeKeyImpl.ARRAY_ELEMENT_TYPE);
                int count = (Integer) type.getAttribute(TypeKeyImpl.ARRAY_ELEMENT_COUNT);

                System.out.println(INDENT + "--- 索引类型  ---");
                printType(indexType);

                // 仅仅在类型匿名的时候才打印基类型信息
                if (indexType.getIdentifier() == null) {
                    printTypeDetail(indexType, recordTypes);
                }

                System.out.println(INDENT + "--- 元素类型 ---");
                printType(elementType);
                System.out.println(INDENT.toString() +"共有" +  count + " 个元素");

                // 仅仅在类型匿名的时候才打印基类型信息
                if (elementType.getIdentifier() == null) {
                    printTypeDetail(elementType, recordTypes);
                }

                break;
            }

            case RECORD: {
                recordTypes.add(type);
                break;
            }
        }
    }
    /**
     * 打印程式中定义的记录类型
     */
    private void printRecords(ArrayList<TypeSpec> recordTypes)
    {
        for (TypeSpec recordType : recordTypes) {
            SymTabEntry recordId = recordType.getIdentifier();
            String name = recordId != null ? recordId.getName() : "<匿名>";

            System.out.println("\n--- 记录类型  " + name + " ---");
            printColumnHeadings();

            // 打印记录所对应符号表中的项
            SymTab symTab = (SymTab) recordType.getAttribute(TypeKeyImpl.RECORD_SYMTAB);
            ArrayList<TypeSpec> newRecordTypes = new ArrayList<TypeSpec>();
            printSymTab(symTab, newRecordTypes);

            // 记录定义里面嵌套的记录
            if (newRecordTypes.size() > 0) {
                printRecords(newRecordTypes);
            }
        }
    }
    /**
     * 将值转化成为字符串
     */
    private String valueToString(Object value)
    {
        return value instanceof String ? "'" + (String) value + "'"
                                       : value.toString();
    }
    /**
     * 打印表的列抬头
     */
    private void printColumnHeadings()
    {
    	 System.out.println();
         System.out.println(String.format(NAME_FORMAT, "标识符 ")
                            + NUMBERS_LABEL +     "      类型说明");
         System.out.println(String.format(NAME_FORMAT, "----------")
                            + NUMBERS_UNDERLINE + "------------------");
    }

    /**
     * 打印符号表中过的每个项
     * @param SymTab 符号表
     */
    private void printSymTab(SymTab symTab)
    {
        //得到只读有序的所有表项
        Collection<SymTabEntry> sorted = symTab.sortedEntries();
        for (SymTabEntry entry : sorted) {
            ArrayList<Integer> lineNumbers = entry.getLineNumbers();
            // 先identifier名字，然后是行数
            System.out.print(String.format(NAME_FORMAT, entry.getName()));
            if (lineNumbers != null) {
                for (Integer lineNumber : lineNumbers) {
                    System.out.print(String.format(NUMBER_FORMAT, lineNumber));
                }
            }
            System.out.println();
        }
    }
}
